package cn.kgc.util.resultutil.advice;

import cn.kgc.util.resultutil.Result;
import cn.kgc.util.resultutil.ResultConstant;
import cn.kgc.util.resultutil.exception.HttpException;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author YC
 * 将异常信息封装到Result对象再封装到ResponseEntity响应实体对象中
 * ControllerAdvice("cn.kgc.controller")注解 表示给对应控制器添加增强（封装异常）
 */
@ControllerAdvice("cn.kgc.controller")
public class ExceptionAdvice {

    /**
     * 捕获参数验证异常
     *
     * @param exception 参数验证异常
     * @return 封装后的响应实体
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Result<Object>> handleMethodException2(MethodArgumentNotValidException exception) {
        BindingResult bindingResult = exception.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        Map<String, String> errorResult = new HashMap<>();
        for (FieldError fieldError : fieldErrors) {
            String fieldName = fieldError.getField();
            String filedErrorMsg = fieldError.getDefaultMessage();
            errorResult.put(fieldName, filedErrorMsg);
        }
        Result<Object> result = Result.error(exception, errorResult);
        return getResponseEntity(result);
    }

    /**
     * 捕获参数验证异常
     *
     * @param exception 参数验证异常
     * @return 封装后的响应实体
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<Result<Object>> handleConstraintViolation(ConstraintViolationException exception) {
        Map<String, String> errors = new HashMap<>();
        for (ConstraintViolation<?> violation : exception.getConstraintViolations()) {
            String field = violation.getPropertyPath().toString().split("\\.")[1];
            errors.put(field, violation.getMessage());
        }
        return getResponseEntity(Result.error(exception, errors));
    }

    /**
     * 捕获自定义异常
     * ExceptionHandler(HttpException.class)注解 表示拦截指定异常
     *
     * @param httpException 自定义异常
     * @return 封装后的响应实体
     */
    @ExceptionHandler(HttpException.class)
    public ResponseEntity<Result<Object>> handleHttpException(HttpException httpException) {
        return getResponseEntity(Result.error(httpException));
    }

    /**
     * 捕获SpringSecurity的权限异常
     */
    @ExceptionHandler(AccessDeniedException.class)
    public ResponseEntity<Result<Object>> handleAccessDeniedException(AccessDeniedException exception) {
        return getResponseEntity(Result.error(new HttpException(ResultConstant.ACCESS_DENIED)));
    }

    /**
     * 捕获其他异常
     * ExceptionHandler(HttpException.class)注解 表示拦截指定异常
     *
     * @param exception 其他异常（检查异常）
     * @return 响应实体
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Result<Object>> handelException(Exception exception) {
        return getResponseEntity(Result.error(exception));
    }

    /**
     * 将Result对象封装到ResponseEntity响应实体对象中的方法
     *
     * @param result 响应实体
     * @return 封装后的响应实体
     */
    private ResponseEntity<Result<Object>> getResponseEntity(Result<Object> result) {
        HttpStatus httpStatus = HttpStatus.OK;
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        return new ResponseEntity<>(result, httpHeaders, httpStatus);
    }
}